// DataFile.cpp: implementation of the CDataFile and 
//               CNamedVariable classes.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <pgl/algotools/DataFile.h>
#include <pgl/algotools/StlAlgo.h>
#include <Afxpriv.h>
#include <fstream>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

namespace parsers
{
	
	// Standard Constructor
	CDataFile::CDataFile()
	{
		m_chDelimiter			= ',';
		m_szFilePath			= _T("")	;
		m_bOKtoRead				= false	;
	}
	
	// Function SetFilePathAndDelimiterTo()
	// Desc: Sets the path to the data file and the delimiter used to seperate the data.
	// Note: Changed to private member in v1.2.
	// Args:
	//- szFilePath  : the fully qualified path to the file
	//- chDelimiter : the delimiter for the file. (',' by default)
	bool CDataFile::SetFilePathAndDelimiterTo(LPCTSTR szFilePath, TCHAR chDelimiter)
	{
		m_chDelimiter = chDelimiter;
		
		if(mf_FileExists(static_cast<LPCTSTR>(szFilePath)))
		{
			m_szFilePath = szFilePath;
			m_bOKtoRead  = true;
			return true;
		}
		
		else
		{
			TRACE(_T("Error: File \"%s\" not found!"),szFilePath);
			return false;
			
		}
	}
	
	// Function ReadDataFile()
	// Desc: Reads the data file.
	// Args:
	//- szFilePath  : the fully qualified path to the file
	//- chDelimiter : the delimiter for the file. (',' by default)
	//- ulCDataFileFormat : the file format - CDATAFILE_CSV_FORMAT by default. 
	//[ Optional formats: CDATAFILE_CSV_FORMAT or CDATAFILE_MTX_FORMAT ]
	bool CDataFile::ReadDataFile(LPCTSTR szFilePath, TCHAR chDelimiter)
	{
		USES_CONVERSION;
		// This class will eventually accept the MatrixX File Format
		// but does not do so in this implementation.  Currently CSV
		// (or other text-delimited) format is the only one accepted.
		int nVars;
		
		// first we ste the path and delimiter.
		// if the file exists and everything seems
		// valid, m_bOKtoRead is set to true.
		if(!SetFilePathAndDelimiterTo(szFilePath, chDelimiter))
			return false;
		// check to see if file is ready to read.
		if(m_bOKtoRead)
		{
			int i = 0;
			// create an ifstream and open the file
			std::ifstream theCSV;
			theCSV.open(T2CA(m_szFilePath));
			
			// count the number of variables
			// we start the count at 1
			i = 1;
			do
			{
				if (theCSV.peek() == m_chDelimiter)
					i++;
			} while (theCSV.get() != '\n');
			
			nVars = i;
			
			// make sure data columns are equal to m_nVars
			// in order to prevent mis-aligned data
			theCSV.ignore(MAX_LINE_BUFFER,'\n');
			i = 1;
			do
			{
				if (theCSV.peek() == m_chDelimiter)
					i++;
			} while (theCSV.get() != '\n');
			
			if(i != nVars)
			 return false;
			
			// create variables and fill in the names...
			i = 0;
			char buff[MAX_FIELD_BUFFER]  = {0};
			char tstr2[MAX_FIELD_BUFFER] = {0};
			CString strBuff = "";
			// rewind the file
			theCSV.close();
			theCSV.open(T2CA(m_szFilePath), std::ios::in | std::ios::binary);
			
			m_nvVariables.resize(nVars);
			for (i = 0; i<nVars ; i++)
			{
				if (i == nVars - 1)
				{ 
					theCSV.getline(buff, MAX_FIELD_BUFFER, '\n');
					sprintf(tstr2 , "%s" , buff);
					strBuff = A2CT(tstr2);
					strBuff = strBuff.Left(strBuff.GetLength()-1);
					m_nvVariables[i].first.Format(_T("%s"),strBuff);
				}
				else
				{ 
					theCSV.getline(buff, MAX_FIELD_BUFFER, m_chDelimiter);
					m_nvVariables[i].first.Format(_T("%s"),A2CT(buff));
				}
			}

			// ok, variables are created and named, now fill 'em up with data...
			i = 0;
			int j = 0;
			// rewind the file again.
			theCSV.close();
			theCSV.open(T2CA(m_szFilePath), std::ios::in | std::ios::binary);
			
			// skip the header line
			theCSV.ignore(MAX_LINE_BUFFER, '\n');
			
			do
			{
				for (i = 0; i<nVars; i++)
				{
					if (i == nVars - 1) // denotes the end of the line
					{ 
						theCSV.getline(buff, MAX_FIELD_BUFFER, '\n');
						m_nvVariables[i].second.push_back( static_cast<float>(atof(buff)) );
					}
					
					else
					{	
						theCSV.getline(buff, MAX_FIELD_BUFFER, m_chDelimiter);
						m_nvVariables[i].second.push_back(static_cast<float>(atof(buff)));
					}
				}
				
				j++;
				
			} while (!theCSV.eof());

			for (i=0;i<nVars;i++)
			{
				if (m_nvVariables[i].second.size())
					m_nvVariables[i].second.pop_back();
			}
			
		} // end if(m_bOKtoRead)
		
		else
			return false;
		
		return true;
}


void CDataFile::ToString(CString& str, LPCTSTR szEOL)
{
	CString format;
	size_t i,j;
	const size_t vsize=m_nvVariables.size();
	const size_t vlinesize=(vsize) ? m_nvVariables[0].second.size() : 0;

	str.Empty();

	for (j = 0;j< vsize;++j)
	{
		str+=m_nvVariables[j].first;
		if(j<vsize-1)
			str+=_T(", ");
		else
			str+=szEOL;
	}
	
	for (i = 0;i<vlinesize;++i)
	{
		for (j = 0;j<vsize;++j)
		{
			format.Format(_T("%g"),m_nvVariables[j].second[i]);
			str+=format;
			if(j<vsize-1)
				str+=_T(", ");
			else
				str+=szEOL;
		}
	}
}

// Function GetFilePath()
// Desc: Gets the fully qualified path to the current data file.
// Args:
//- none
// returns: the filename of the current data file
LPCTSTR CDataFile::GetFilePath()  const
{
	try
	{
		if(m_bOKtoRead)
			return m_szFilePath;
		else
			throw _T("Error - Filename and path not set!");
	}
	catch(const TCHAR*)
	{
		return _T("Error - File undefined");
	}	
	catch(...)
	{
		TRACE(_T("Unknown error in CDataFile::GetFilePath()"));
		return _T("Error - Unknown");
	}
}

// Function GetFilePath()
// Desc: Checks to see if a file exists or not.
// Args:
//- lpszFilePath: the fully qualified path to a file
// returns: true if the file exists, false if it doesn't
bool CDataFile::mf_FileExists(LPCTSTR lpszFilePath)
{
	WIN32_FIND_DATA findFileData;
	HANDLE hFind = FindFirstFile(lpszFilePath, &findFileData);	
	
	if (hFind == INVALID_HANDLE_VALUE)	
		return false;	
	
	VERIFY(FindClose(hFind));
	return true;
}

// destructor
CDataFile::~CDataFile()
{
	m_nvVariables.clear();
}

};
